/*
 * 11/19/04 1.0 moved to LGPL.
 * 02/23/99 JavaConversion by E.B
 * Don Cross, April 1993.
 * RIFF file format classes.
 * See Chapter 8 of "Multimedia Programmer's Reference" in
 * the Microsoft Windows SDK.
 *  
 *-----------------------------------------------------------------------
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */

package javazoom.jl.converter;

/**
 * Class allowing WaveFormat Access
 */
public class WaveFile extends RiffFile
{
	public static final int 	MAX_WAVE_CHANNELS = 2;

	class WaveFormat_ChunkData
	{
		public short         wFormatTag = 0;       // Format category (PCM=1)
		public short         nChannels = 0;        // Number of channels (mono=1, stereo=2)
		public int         	nSamplesPerSec = 0;   // Sampling rate [Hz]
		public int         	nAvgBytesPerSec = 0;
		public short         nBlockAlign = 0;
		public short         nBitsPerSample = 0;

		public WaveFormat_ChunkData()
		{
			wFormatTag = 1;     // PCM
			Config(44100,(short)16,(short)1);
		}

		public void Config (int NewSamplingRate, short NewBitsPerSample, short NewNumChannels)
		{
			nSamplesPerSec  = NewSamplingRate;
			nChannels       = NewNumChannels;
			nBitsPerSample  = NewBitsPerSample;
			nAvgBytesPerSec = (nChannels * nSamplesPerSec * nBitsPerSample) / 8;
			nBlockAlign     = (short) ((nChannels * nBitsPerSample) / 8);
		}
	}


	class WaveFormat_Chunk
	{
		public RiffChunkHeader         header;
		public WaveFormat_ChunkData    data;

		public WaveFormat_Chunk()
		{
			header = new RiffChunkHeader();
			data = new WaveFormat_ChunkData();
			header.ckID     =   FourCC("fmt ");
			header.ckSize   =   16;
		}

		public int VerifyValidity()
		{
			boolean ret = header.ckID == FourCC("fmt ") &&

					(data.nChannels == 1 || data.nChannels == 2) &&

					data.nAvgBytesPerSec == ( data.nChannels *
							data.nSamplesPerSec *
							data.nBitsPerSample    ) / 8   &&

					data.nBlockAlign == ( data.nChannels *
							data.nBitsPerSample ) / 8;
			if (ret == true) return 1;
			else return 0;
		}
	}

	public class WaveFileSample
	{
		public short[] 				chan;

		public WaveFileSample()
		{chan = new short[WaveFile.MAX_WAVE_CHANNELS];}
	}

	private WaveFormat_Chunk   	 wave_format;
	private RiffChunkHeader    	 pcm_data;
	private long            		 pcm_data_offset = 0;  // offset of 'pcm_data' in output file
	private int 	           		 num_samples = 0;


	/**
	 * Constructs a new WaveFile instance.
	 */
	public WaveFile()
	{
		pcm_data = new RiffChunkHeader();
		wave_format = new WaveFormat_Chunk();
		pcm_data.ckID = FourCC("data");
		pcm_data.ckSize = 0;
		num_samples = 0;
	}

	/**
	 *
	 *
	 public int OpenForRead (String Filename)
	 {
	 // Verify filename parameter as best we can...
	 if (Filename == null)
	 {
	 return DDC_INVALID_CALL;
	 }
	 int retcode = Open ( Filename, RFM_READ );

	 if ( retcode == DDC_SUCCESS )
	 {
	 retcode = Expect ( "WAVE", 4 );

	 if ( retcode == DDC_SUCCESS )
	 {
	 retcode = Read(wave_format,24);

	 if ( retcode == DDC_SUCCESS && !wave_format.VerifyValidity() )
	 {
	 // This isn't standard PCM, so we don't know what it is!
	 retcode = DDC_FILE_ERROR;
	 }

	 if ( retcode == DDC_SUCCESS )
	 {
	 pcm_data_offset = CurrentFilePosition();

	 // Figure out number of samples from
	 // file size, current file position, and
	 // WAVE header.
	 retcode = Read (pcm_data, 8 );
	 num_samples = filelength(fileno(file)) - CurrentFilePosition();
	 num_samples /= NumChannels();
	 num_samples /= (BitsPerSample() / 8);
	 }
	 }
	 }
	 return retcode;
	 }*/

	/**
	 *
	 */
	public int OpenForWrite (String Filename, int SamplingRate, short BitsPerSample, short NumChannels)
	{
		// Verify parameters...
		if ( (Filename==null) ||
				(BitsPerSample != 8 && BitsPerSample != 16) ||
				NumChannels < 1 || NumChannels > 2 )
		{
			return DDC_INVALID_CALL;
		}

		wave_format.data.Config ( SamplingRate, BitsPerSample, NumChannels );

		int retcode = Open ( Filename, RFM_WRITE );

		if ( retcode == DDC_SUCCESS )
		{
			byte [] theWave = {(byte)'W',(byte)'A',(byte)'V',(byte)'E'};
			retcode = Write ( theWave, 4 );

			if ( retcode == DDC_SUCCESS )
			{
				// Ecriture de wave_format
				retcode = Write (wave_format.header, 8);
				retcode = Write (wave_format.data.wFormatTag, 2);
				retcode = Write (wave_format.data.nChannels, 2);
				retcode = Write (wave_format.data.nSamplesPerSec, 4);
				retcode = Write (wave_format.data.nAvgBytesPerSec, 4);
				retcode = Write (wave_format.data.nBlockAlign, 2);
				retcode = Write (wave_format.data.nBitsPerSample, 2);
            /* byte[] br = new byte[16];
	        br[0] = (byte) ((wave_format.data.wFormatTag >> 8) & 0x00FF);
        	br[1] = (byte) (wave_format.data.wFormatTag & 0x00FF);

        	br[2] = (byte) ((wave_format.data.nChannels >> 8) & 0x00FF);
	        br[3] = (byte) (wave_format.data.nChannels & 0x00FF);

	        br[4] = (byte) ((wave_format.data.nSamplesPerSec >> 24)& 0x000000FF);
	        br[5] = (byte) ((wave_format.data.nSamplesPerSec >> 16)& 0x000000FF);
	        br[6] = (byte) ((wave_format.data.nSamplesPerSec >> 8)& 0x000000FF);
	        br[7] = (byte) (wave_format.data.nSamplesPerSec & 0x000000FF);

	        br[8] = (byte) ((wave_format.data.nAvgBytesPerSec>> 24)& 0x000000FF);
	        br[9] = (byte) ((wave_format.data.nAvgBytesPerSec >> 16)& 0x000000FF);
	        br[10] = (byte) ((wave_format.data.nAvgBytesPerSec >> 8)& 0x000000FF);
	        br[11] = (byte) (wave_format.data.nAvgBytesPerSec & 0x000000FF);

	        br[12] = (byte) ((wave_format.data.nBlockAlign >> 8) & 0x00FF);
	        br[13] = (byte) (wave_format.data.nBlockAlign & 0x00FF);

	        br[14] = (byte) ((wave_format.data.nBitsPerSample >> 8) & 0x00FF);
	        br[15] = (byte) (wave_format.data.nBitsPerSample & 0x00FF);
   		 	retcode = Write (br, 16); */


				if ( retcode == DDC_SUCCESS )
				{
					pcm_data_offset = CurrentFilePosition();
					retcode = Write ( pcm_data, 8 );
				}
			}
		}

		return retcode;
	}

	/**
	 *
	 *
	 public int ReadSample ( short[] Sample )
	 {

	 }*/

	/**
	 *
	 *
	 public int WriteSample( short[] Sample )
	 {
	 int retcode = DDC_SUCCESS;
	 switch ( wave_format.data.nChannels )
	 {
	 case 1:
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 pcm_data.ckSize += 1;
	 retcode = Write ( Sample, 1 );
	 break;

	 case 16:
	 pcm_data.ckSize += 2;
	 retcode = Write ( Sample, 2 );
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }
	 break;

	 case 2:
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 retcode = Write ( Sample, 1 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 // &Sample[1]
	 retcode = Write (Sample, 1 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 pcm_data.ckSize += 2;
	 }
	 }
	 break;

	 case 16:
	 retcode = Write ( Sample, 2 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 // &Sample[1]
	 retcode = Write (Sample, 2 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 pcm_data.ckSize += 4;
	 }
	 }
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }

	 return retcode;
	 }*/

	/**
	 *
	 *
	 public int SeekToSample ( long SampleIndex )
	 {
	 if ( SampleIndex >= NumSamples() )
	 {
	 return DDC_INVALID_CALL;
	 }
	 int SampleSize = (BitsPerSample() + 7) / 8;
	 int rc = Seek ( pcm_data_offset + 8 +
	 SampleSize * NumChannels() * SampleIndex );
	 return rc;
	 }*/

	/**
	 * Write 16-bit audio
	 */
	public int WriteData ( short[] data, int numData )
	{
		int extraBytes = numData * 2;
		pcm_data.ckSize += extraBytes;
		return super.Write ( data, extraBytes );
	}

	/**
	 * Read 16-bit audio.
	 *
	 public int ReadData  (short[] data, int numData)
	 {return super.Read ( data, numData * 2);} */

	/**
	 * Write 8-bit audio.
	 *
	 public int WriteData ( byte[] data, int numData )
	 {
	 pcm_data.ckSize += numData;
	 return super.Write ( data, numData );
	 }*/

	/**
	 * Read 8-bit audio.
	 *
	 public int ReadData ( byte[] data, int numData )
	 {return super.Read ( data, numData );} */


	/**
	 *
	 *
	 public int ReadSamples  (int num, int [] WaveFileSample)
	 {

	 }*/

	/**
	 *
	 *
	 public int WriteMonoSample ( short[] SampleData )
	 {
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 pcm_data.ckSize += 1;
	 return Write ( SampleData, 1 );

	 case 16:
	 pcm_data.ckSize += 2;
	 return Write ( SampleData, 2 );
	 }
	 return DDC_INVALID_CALL;
	 }*/

	/**
	 *
	 *
	 public int WriteStereoSample  ( short[] LeftSample, short[] RightSample )
	 {
	 int retcode = DDC_SUCCESS;
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 retcode = Write ( LeftSample, 1 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 retcode = Write ( RightSample, 1 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 pcm_data.ckSize += 2;
	 }
	 }
	 break;

	 case 16:
	 retcode = Write ( LeftSample, 2 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 retcode = Write ( RightSample, 2 );
	 if ( retcode == DDC_SUCCESS )
	 {
	 pcm_data.ckSize += 4;
	 }
	 }
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }
	 return retcode;
	 }*/

	/**
	 *
	 *
	 public int ReadMonoSample ( short[] Sample )
	 {
	 int retcode = DDC_SUCCESS;
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 byte[] x = {0};
	 retcode = Read ( x, 1 );
	 Sample[0] = (short)(x[0]);
	 break;

	 case 16:
	 retcode = Read ( Sample, 2 );
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }
	 return retcode;
	 }*/

	/**
	 *
	 *
	 public int ReadStereoSample ( short[] LeftSampleData, short[] RightSampleData )
	 {
	 int retcode = DDC_SUCCESS;
	 byte[] x = new byte[2];
	 short[] y = new short[2];
	 switch ( wave_format.data.nBitsPerSample )
	 {
	 case 8:
	 retcode = Read ( x, 2 );
	 L[0] = (short) ( x[0] );
	 R[0] = (short) ( x[1] );
	 break;

	 case 16:
	 retcode = Read ( y, 4 );
	 L[0] = (short) ( y[0] );
	 R[0] = (short) ( y[1] );
	 break;

	 default:
	 retcode = DDC_INVALID_CALL;
	 }
	 return retcode;
	 }*/


	/**
	 *
	 */
	public int Close()
	{
		int rc = DDC_SUCCESS;

		if ( fmode == RFM_WRITE )
			rc = Backpatch ( pcm_data_offset, pcm_data, 8 );
		if ( rc == DDC_SUCCESS )
			rc = super.Close();
		return rc;
	}

	// [Hz]
	public int SamplingRate()
	{return wave_format.data.nSamplesPerSec;}

	public short BitsPerSample()
	{return wave_format.data.nBitsPerSample;}

	public short NumChannels()
	{return wave_format.data.nChannels;}

	public int NumSamples()
	{return num_samples;}


	/**
	 * Open for write using another wave file's parameters...
	 */
	public int OpenForWrite (String Filename, WaveFile OtherWave )
	{
		return OpenForWrite ( Filename,
				OtherWave.SamplingRate(),
				OtherWave.BitsPerSample(),
				OtherWave.NumChannels() );
	}

	/**
	 *
	 */
	public long CurrentFilePosition()
	{
		return super.CurrentFilePosition();
	}

   /* public int FourCC(String ChunkName)
   {
      byte[] p = {0x20,0x20,0x20,0x20};
	  ChunkName.getBytes(0,4,p,0);
	  int ret = (((p[0] << 24)& 0xFF000000) | ((p[1] << 16)&0x00FF0000) | ((p[2] << 8)&0x0000FF00) | (p[3]&0x000000FF));
      return ret;
   }*/

}